#pragma once

#include <cstddef>
#include <limits>

#include "minmax.h"

namespace mdtx::online::impl
{
    template <typename T>
    class count_nz
    {
    public:
        ptrdiff_t n, m;

        void insert_impl(T x)
        {
            n += 1;
            if (x != 0)
                m += 1;
        }

        void remove_impl(T x)
        {
            n -= 1;
            if (x != 0)
                m -= 1;
        }

        void reset_impl()
        {
            m = n = 0;
        }
    };

    template <typename T>
    class count_n
    {
    public:
        ptrdiff_t n;

        void insert_impl(T)
        {
            n += 1;
        }

        void remove_impl(T)
        {
            n -= 1;
        }

        void reset_impl()
        {
            n = 0;
        }
    };

    template <typename T, bool S>
    class count_minmax;

    template <typename T>
    class count_minmax<T, false>
    {
    public:
        T v_min{std::numeric_limits<T>::max()}, v_max{std::numeric_limits<T>::lowest()};
        ptrdiff_t n_min{}, n_max{};

        void insert_impl(T x)
        {
            if (x <= v_min)
            {
                if (x == v_min)
                {
                    n_min += 1;
                }
                else
                {
                    v_min = x;
                    n_min = 1;
                }
            }
            if (x >= v_max)
            {
                if (x == v_max)
                {
                    n_max += 1;
                }
                else
                {
                    v_max = x;
                    n_max = 1;
                }
            }
        }

        void reset_impl()
        {
            v_min = std::numeric_limits<T>::max();
            v_max = std::numeric_limits<T>::lowest();
            n_min = n_max = 0;
        }
    };

    template <typename T>
    class count_minmax<T, true> : public minmax<T>
    {
    public:
        ptrdiff_t count_min() const
        {
            ptrdiff_t n{};
            if (!this->mindeq.empty())
            {
                auto vmin = this->mindeq.front();
                for (auto elem : this->mindeq)
                {
                    if (elem == vmin)
                    {
                        n += 1;
                    }
                    else
                    {
                        break;
                    }
                }
            }
            return n;
        }

        ptrdiff_t count_max() const
        {
            ptrdiff_t n{};
            if (!this->maxdeq.empty())
            {
                auto vmax = this->maxdeq.front();
                for (auto elem : this->maxdeq)
                {
                    if (elem == vmax)
                    {
                        n += 1;
                    }
                    else
                    {
                        break;
                    }
                }
            }
            return n;
        }
    };
}
